#!/usr/bin/env python
# -*- coding: utf-8 -*-
# @Time    : 2022/10/29 22:58
# @Author  : when
# @File    : dispatch.py
# @Description :

from abc import get_cache_token
from types import MappingProxyType
from typing import Callable, Hashable
from functools import update_wrapper


def func_dispatch(func) -> Callable:
    registry = {}
    cache_token = None

    def register(key: Hashable, fun: Callable = None):
        """
        注册函数，将函数对象注册到registry中
        :param key:
        :param fun:
        :return:
        """
        nonlocal cache_token
        if not fun:
            return lambda f: register(key, f)
        registry[key] = fun
        if not cache_token and hasattr(key, '__abstractmethods__'):
            cache_token = get_cache_token()
        return fun

    def dispatch(key: Hashable):
        """
        从registry中获取指定的注册函数
        :param key:
        :return:
        """
        nonlocal cache_token
        if cache_token:
            current_token = get_cache_token()
            if cache_token != current_token:
                cache_token = current_token
        try:
            impl = registry[key]
        except KeyError:
            impl = registry[object]
        return impl

    def wrapper(*args, **kwargs):
        if not args:
            raise TypeError(f'{func_name} requires at least 1 positional argument.')
        # 获取并调用已注册的函数
        return dispatch(args[0])(*args, **kwargs)

    func_name = getattr(func, '__name__', 'when.func_dispatch.func')
    registry[object] = func
    wrapper.register = register
    wrapper.dispatch = dispatch
    wrapper.registry = MappingProxyType(registry)  # 限制外部对registry属性仅读
    update_wrapper(wrapper, func)
    return wrapper
